/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package domain;

import expression.interval.IntValue;
import expression.interval.Interval;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author ddoose
 */
public class Domain {
    public static final Domain TRUE = new Domain(Interval.createValue(1));
    public static final Domain FALSE = new Domain(Interval.createValue(0));
    public static final Domain BOOLEAN = new Domain(Interval.createBounded(0, 1));
    public static final Domain UNIVERSE = new Domain(Interval.createUniverse());
    public static final Domain EMPTY = new Domain();
    
    public List<Interval> domain;

    public Domain() {
        domain = new ArrayList<Interval>();
    }
    
    public Domain(Interval i) {
        domain = new ArrayList<Interval>();
        domain.add(i);
    }
    
    public Domain(int i) {
        domain = new ArrayList<Interval>();
        domain.add(Interval.createValue(i));        
    }

    public Domain(int i, int j) {
        domain = new ArrayList<Interval>();
        domain.add(Interval.createBounded(i, j));        
    }

    public boolean isEmpty() {
        return domain.size() == 0;
    }
    
    public boolean isUniverse() {
        return (domain.size() > 0) && (domain.get(0).isUniverse());
    }
    
    public boolean isLeftBounded() {
        return (domain.size() > 0) && domain.get(0).isLeftBounded();
    }
    
    public boolean isRightBounded() {
        return (domain.size() > 0) && domain.get(domain.size()-1).isRightBounded();
    }
    
    public boolean isValue() {
        return (domain.size() == 1) && (domain.get(0) instanceof IntValue);
    }
    
    public int getLeftBound() {
        return domain.get(0).getLeftBound();
    }
    
    public int getRightBound() {
        return domain.get(domain.size()-1).getRightBound();
    }
    
    public int getValue() {
        return domain.get(0).getValue();
    }
    
    public Domain inter(Interval i) {
        if (i.isUniverse()) {
            return clone();
        }
        Domain d = new Domain();
        
        for (Interval j: domain) {
            Interval k = i.inter(j);

            if (!k.isEmpty()) {
                d.domain.add(k);
            }
        }
        
        return d;
    }
    
    public Domain inter(Domain d) {
        if (isEmpty() || d.isEmpty()) {
            return new Domain();
        }
        if (isUniverse()) {
            return d.clone();
        }
        if (d.isUniverse()) {
            return clone();
        }
        
        int i, j;
        i = j = 0;
        Domain newD = new Domain();
        
        while ((i < domain.size()) && (j < d.domain.size())) {
            Interval inter1, inter2;
            inter1 = domain.get(i);
            inter2 = d.domain.get(j);

            if (inter1.isStrictLeft(inter2)) {
                i++;
            } else if (inter2.isStrictLeft(inter1)) {
                j++;
            } else {
                newD.domain.add(inter1.inter(inter2));
                if (inter1.isRight(inter2)) {
                    j++;
                }
                else if (inter2.isRight(inter1)) {
                    i++;
                }
                else {
                    i++;
                    j++;
                }
            }
        }
        
        return newD;
    }
    
    public Domain union(Domain d) {
        if (isUniverse() || d.isEmpty()) {
            return clone();
        }
        else if (isEmpty() || d.isUniverse()) {
            return d.clone();
        }
        
        int i, j;
        i = j = 0;
        Domain newD = new Domain();
        Interval newU = null;
        
        while ((i < domain.size()) || (j < d.domain.size())) {
            Interval u1, u2, u, uu;
            boolean bi = true;
            
            if (i < domain.size()) {
                u1 = domain.get(i);
            }
            else {
                u1 = null;
            }
            if (j < d.domain.size()) {
                u2 = d.domain.get(j);
            }
            else {
                u2 = null;
            }
            
            if ((u1 != null) && ((u2 == null) || (u1.isLeft(u2)))) {
                u = u1;
                bi = true;
            }
            else {
                u = u2;
                bi = false;
            }
                
            if (newU == null) {
                newU = u.clone();
            }
            else {
                uu = newU.union(u);
                if (uu == null) {
                    newD.domain.add(newU);
                    newU = u;
                }
                else {
                    newU = uu;
                }
            }
            
            if (bi) {
                i++;
            }
            else {
                j++;
            }
        }
        newD.domain.add(newU);
        
        return newD;        
    }
    
    public Domain comp() {
        Domain d = new Domain();
        
        if (isEmpty()) {
            return Domain.UNIVERSE;
        }
        else if (isUniverse()) {
            return Domain.EMPTY;
        }
        
        int i = 0;
        Interval inter = domain.get(i);
        Interval inter2 = inter;
        if (inter.isLeftBounded()) {
            d.domain.add(Interval.createRightBounded(inter.getLeftBound()-1));
        }
        
        while (i < domain.size()-1) {
            inter = domain.get(i++);
            inter2 = domain.get(i);
            d.domain.add(Interval.createBounded(inter.getRightBound()+1, inter2.getLeftBound()-1));
        }
        
        if (inter2.isRightBounded()) {
            d.domain.add(Interval.createLeftBounded(inter2.getRightBound()+1));
        }
        
        return d;
    }
    
    public int size() {
        if (isLeftBounded() && isRightBounded()) {
            int size = 0;
            for (Interval inter: domain) {
                size += (inter.getRightBound() - inter.getLeftBound());
            }
            return size;
        }
        return -1;
    }
    
    public int intervalNumber() {
        return domain.size();
    }
    
    public Interval getInterval(int i) {
        return domain.get(i);
    }
        
    public Domain clone() {
        Domain d = new Domain();
        
        for (Interval i: domain) {
            d.domain.add(i.clone());
        }
        
        return d;
    }
    
    public boolean equals(Domain d) {
        if (domain.size() != d.domain.size()) {
            return false;
        }
        for (int i = 0; i < domain.size(); i++) {
            if (!domain.get(i).equals(d.domain.get(i))) {
                return false;
            }
        }
        return true;
    }
    
    public boolean isTrue() {
        return (domain.size() == 1) && (domain.get(0) instanceof IntValue) && (domain.get(0).getLeftBound() == 1);
    }
    
    public boolean isFalse() {
        return (domain.size() == 1) && (domain.get(0) instanceof IntValue) && (domain.get(0).getLeftBound() == 0);
    }
    
    public boolean disjoint(Domain d) {
        return inter(d).isEmpty();
    }
    
    public String toString() {
        String s = "{";
        for (Interval i: domain) {
            s += " " + i;
        }
        s += " }";
        
        return s;
    }
    
    public static void main(String[] args) {
        Domain d1 = new Domain();
        d1.domain.add(Interval.createBounded(2, 8));
        d1.domain.add(Interval.createLeftBounded(14));
        
        Domain d2 = new Domain();
        d2.domain.add(Interval.createRightBounded(4));
        d2.domain.add(Interval.createBounded(6, 10));
        d2.domain.add(Interval.createBounded(12, 16));
        
        System.out.println("d1: " + d1);
        System.out.println("d2: " + d2);

        System.out.println("d1 I d2: " + d1.inter(d2));
        System.out.println("d1 U d2: " + d1.union(d2));
        
        System.out.println("C(d1): " + d1.comp());
        System.out.println("C(d2): " + d2.comp());
    }
}
